

/**
 * @author johannes.nel@gmail.com (Johan)
 * @fileoverview The base model which takes an id in its constructer.
 */
goog.provide('lfpug.model.BaseModel');
goog.provide('lfpug.model.BaseModel.ChangedProperty');
goog.provide('lfpug.model.BaseModel.EventType');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('goog.functions');
goog.require('goog.ui.IdGenerator');
goog.require('lfpug.events.ModelChangeEvent');



/**
 * @constructor
 * @extends {goog.events.EventTarget}
 *
 * @param {string} id
 *        The objects unique id.
 */
lfpug.model.BaseModel = function(id) {
  goog.base(this);

  this.setId(id);
  /**
   * @private
   * @type {boolean}
   */
  this.willInvalidate_ = false;
  this.eventPropegationFunc_ = goog.bind(this.propegateInternal_, this);
  /**
   * @private
   */
  this.changedProps_ = [];
  this.changedPropsHash_ = {};
};
goog.inherits(lfpug.model.BaseModel, goog.events.EventTarget);


/**
 * @typedef {{name: string, prevValue: *, curValue: *}}
 */
lfpug.model.BaseModel.ChangedProperty;


/**
 * @private
 * @type {Array.<lfpug.model.BaseModel.ChangedProperty>}
 */
lfpug.model.BaseModel.prototype.changedProps_;


/**
 * Specifies the event types dispatched by BetstreamContest
 * @enum {string}
 */
lfpug.model.BaseModel.EventType = {
  CHANGE: goog.events.getUniqueId('change')
};


/**
 * @param {lfpug.model.ArrayCollection | lfpug.model.BaseModel} obj The object
 * from which events are to be bubbled.
 */
lfpug.model.BaseModel.prototype.registerForBubbeling = function(obj) {
  var events = lfpug.model.BaseModel.getChildObservableEvents_();
  for (var i = 0, l = events.length; i < l; i++) {
    obj.addEventListener(events[i], this.eventPropegationFunc_);
  }
};


/**
 * @param {lfpug.model.ArrayCollection | lfpug.model.BaseModel} obj The object
 * from which events are to be bubbled.
 */
lfpug.model.BaseModel.prototype.deregisterForBubbeling = function(obj) {
  var events = lfpug.model.BaseModel.getChildObservableEvents_();
  for (var i = 0, l = events.length; i < l; i++) {
    obj.removeEventListener(events[i], this.eventPropegationFunc_);
  }
};


/**
 * @private
 * @return {Array.<lfpug.events.ArrayCollectionEvent.EventType>}
 * The events this array collection will bubble through the system.
 */
lfpug.model.BaseModel.getChildObservableEvents_ = function() {
  return [
    lfpug.model.BaseModel.EventType.CHANGE,
    lfpug.events.ArrayCollectionEvent.EventType.CHANGE,
    lfpug.events.ArrayCollectionEvent.EventType.ADD,
    lfpug.events.ArrayCollectionEvent.EventType.REMOVE,
    lfpug.events.ArrayCollectionEvent.EventType.REMOVE_ALL
  ];
};


/**
 * @private
 * @type {string}
 */
lfpug.model.BaseModel.prototype.id_;


/**
 * @param {string} id
 *        The identifier string.
 */
lfpug.model.BaseModel.prototype.setId = function(id) {
  this.id_ = id;
};


/**
 * @return {string}
 *         The model's identifier string.
 */
lfpug.model.BaseModel.prototype.getId = function() {
  return this.id_;
};


/**
 * For this example application I have implemented a synchranous invalidation
 * system. I would recommend doing this async.
 * @protected
 * @param {string=} opt_propName the property which changed.
 * @param {*=} opt_prevValue the property which changeds' old value.
 * @param {*=} opt_curValue the property which changeds' new value.
 */
lfpug.model.BaseModel.prototype.invalidate =
    function(opt_propName, opt_prevValue, opt_curValue) {
  if (goog.isDefAndNotNull(opt_propName)) {
    var changeProp = {name: opt_propName};
    if (goog.isDefAndNotNull(opt_prevValue)) {
      changeProp.prevValue = opt_prevValue;
    }
    if (goog.isDefAndNotNull(opt_curValue)) {
      changeProp.curValue = opt_curValue;
    }

  }else {
    var all = lfpug.model.BaseModel.Property.ALL;
    var changeProp = {name: all};
  }
  var e = new lfpug.events.ModelChangeEvent(
      lfpug.model.BaseModel.EventType.CHANGE, this);
  e.setChangedProp(changeProp);
  this.dispatchEvent(e);
};


/**
 * will propegate all collection events
 * @private
 * @param {lfpug.events.ArrayCollectionEvent} event The event.
 */
lfpug.model.BaseModel.prototype.propegateInternal_ =
    function(event) {
  var arr = event.getChain();
  arr.push(event);
  var e = new lfpug.events.ModelChangeEvent(
      lfpug.model.BaseModel.EventType.CHANGE, this, arr);
  this.dispatchEvent(e);
};


/**
 * @override
 */
lfpug.model.BaseModel.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');
};


/**
 * @enum {string}
 */
lfpug.model.BaseModel.Property = {
  ALL: ''
};
